﻿using ENet;
using huqiang.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace huqiang.Communication
{
    public class ENetServer
    {
        Host _server;
        //byte[] _buffer;
        bool running;
        ushort _port;
        public Thread recvThread;
        public Thread sendThread;
        AutoResetEvent are;
        public int tickRateClient = 60;
        LockBuffer<ENetClient> linkBuff;
        /// <summary>
        /// 白名单
        /// </summary>
        public static List<uint> Whitelist = new List<uint>();
        /// <summary>
        /// 黑名单
        /// </summary>
        public static List<uint> Blacklist = new List<uint>();
        long now, last;
        /// <summary>
        /// 拒绝外部发起的新连接
        /// </summary>
        public bool RejectAutoConnections = false;
        /// <summary>
        /// 创建新的客户端连接
        /// </summary>
        public Func<ENetClient> CreateNew = () => { return new ENetClient(); };
        public void Run(ushort port = 0, int userBuffer = 2048)
        {
            _port = port;
            ENet.Library.Initialize();
            _server = new Host();
            //_buffer = new byte[1500];
            linkBuff = new LockBuffer<ENetClient>(userBuffer);
            Address address = new Address();
            address.Port = port;
            _server.Create(address, userBuffer);
            Start();
        }
        public void Start()
        {
            if (running)
            {
                return;
            }
            
            running = true;
            if (recvThread == null)
            {
                //创建消息接收线程
                recvThread = new Thread(Listen);
                recvThread.Start();
                are = new AutoResetEvent(false);
                sendThread = new Thread(Sending);
                sendThread.Start();
            }
        }
        void Listen()
        {
            while (running)
            {
                try
                {
                    if (_server.Service(1000 / tickRateClient, out ENet.Event netEvent) > 0)
                    {
                        ENetClient link = null;
                        if (RejectAutoConnections)
                        {

                            uint ip = netEvent.Peer.IP4;
                            if (Whitelist.Contains(ip))
                                link = FindOrCreateLink(ip, netEvent.Peer.Port);
                            else link = FindLink(ip, netEvent.Peer.Port);
                        }
                        else
                        {
                            link = FindOrCreateLink(netEvent.Peer.IP4, netEvent.Peer.Port);
                        }
                        if (link != null)
                        {
                            switch (netEvent.Type)
                            {
                                case ENet.EventType.None:
                                    break;
                                case ENet.EventType.Timeout:
                                    link.Connect();
                                    break;
                                case ENet.EventType.Disconnect:
                                    link.Connect();
                                    break;
                                case ENet.EventType.Connect:
                                    link.Disconnect();
                                    break;
                                case ENet.EventType.Receive:
                                    link.Dispatch(netEvent.Packet.Data, netEvent.Packet.Length);
                                    netEvent.Packet.Dispose();
                                    break;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.StackTrace);
                }
            }
        }

        void Sending()
        {
            while (running)
            {
                are.WaitOne(1);
                try
                {
                    SendAll();
                }
                catch (Exception ex)//
                {
                    OutLog.WriteLine(ex.StackTrace);
                }
            }
        }
        /// <summary>
        /// sendThread
        /// </summary>
        public virtual void SendAll()
        {
            //now = System.Diagnostics.Stopwatch.GetTimestamp();//DateTime.Now.Ticks;
            for (int i = 0; i < linkBuff.Count; i++)
            {
                var link = linkBuff[i];
                if (link != null)
                {
                    try
                    {
                        link.SafeSend();
                    }
                    catch { }
                }
            }
        }

        public ENetClient FindLink(uint ip, int port)
        {
            return linkBuff.Find((link) => {
                if (link.ip == ip)
                    if (link.port == port)
                        return true;
                return false;
            });
        }
        public ENetClient FindOrCreateLink(uint ip,ushort port)
        {
            var link = FindLink(ip, port);
            if (link == null)
            {
                link = CreateNew();
                link.ConnectTime = System.Diagnostics.Stopwatch.GetTimestamp();//DateTime.Now.Ticks;
                link.port = port;
                link.ip = ip;
                link.buffIndex = linkBuff.Count;
                linkBuff.Add(link);
                link.Awake();
            }
            return link;
        }
    }
}
